home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Audio, Video & Photo / Songbird 0.7.0 / Songbird_0.7.0_windows-i686-msvc8.exe / components / sbLibraryUtils.jsm < prev    next >
Text File  |  2008-08-06  |  16KB  |  578 lines

  1. /*
  2. //
  3. // BEGIN SONGBIRD GPL
  4. //
  5. // This file is part of the Songbird web player.
  6. //
  7. // Copyright(c) 2005-2008 POTI, Inc.
  8. // http://songbirdnest.com
  9. //
  10. // This file may be licensed under the terms of of the
  11. // GNU General Public License Version 2 (the "GPL").
  12. //
  13. // Software distributed under the License is distributed
  14. // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
  15. // express or implied. See the GPL for the specific language
  16. // governing rights and limitations.
  17. //
  18. // You should have received a copy of the GPL along with this
  19. // program. If not, go to http://www.gnu.org/licenses/gpl.html
  20. // or write to the Free Software Foundation, Inc.,
  21. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  22. //
  23. // END SONGBIRD GPL
  24. //
  25. */
  26. Components.utils.import("resource://app/jsmodules/sbProperties.jsm");
  27. Components.utils.import("resource://app/jsmodules/sbColumnSpecParser.jsm");
  28.  
  29. EXPORTED_SYMBOLS = ["LibraryUtils"];
  30.  
  31. const Cc = Components.classes;
  32. const Ci = Components.interfaces;
  33.  
  34. /**
  35.  * \class LibraryUtils
  36.  * \brief Javascript wrappers for common library tasks
  37.  */
  38. var LibraryUtils = {
  39.  
  40.   get manager() {
  41.     var manager = Cc["@songbirdnest.com/Songbird/library/Manager;1"].
  42.                   getService(Ci.sbILibraryManager);
  43.     if (manager) {
  44.       // Succeeded in getting the library manager, don't do this again.
  45.       this.__defineGetter__("manager", function() {
  46.         return manager;
  47.       });
  48.     }
  49.     return manager;
  50.   },
  51.  
  52.   get mainLibrary() {
  53.     return this.manager.mainLibrary;
  54.   },
  55.  
  56.   get webLibrary() {
  57.     var webLibraryGUID = Cc["@mozilla.org/preferences-service;1"].
  58.                          getService(Ci.nsIPrefBranch).
  59.                          getCharPref("songbird.library.web");
  60.     var webLibrary = this.manager.getLibrary(webLibraryGUID);
  61.     delete webLibraryGUID;
  62.  
  63.     if (webLibrary) {
  64.       // Succeeded in getting the web library, don't ever do this again.
  65.       this.__defineGetter__("webLibrary", function() {
  66.         return webLibrary;
  67.       });
  68.     }
  69.  
  70.     return webLibrary;
  71.   },
  72.   
  73.   getMediaListByGUID: function(aLibraryGUID, aMediaListGUID) {
  74.     var mediaList = (aLibraryGUID instanceof Ci.sbILibrary) ?
  75.                     aLibraryGUID :
  76.                     this.manager.getLibrary( aLibraryGUID );
  77.  
  78.     // Are we loading the root library or a media list within it?
  79.     if (aMediaListGUID && aLibraryGUID != aMediaListGUID) {
  80.       mediaList = mediaList.getMediaItem( aMediaListGUID );  
  81.     }
  82.     return mediaList;
  83.   },
  84.  
  85.   _standardFilterConstraint: null,
  86.   get standardFilterConstraint() {
  87.     if (!this._standardFilterConstraint) {
  88.       this._standardFilterConstraint = this.createConstraint([
  89.         [
  90.           [SBProperties.isList, ["0"]]
  91.         ],
  92.         [
  93.           [SBProperties.hidden, ["0"]]
  94.         ]
  95.       ]);
  96.     }
  97.     return this._standardFilterConstraint;
  98.   },
  99.  
  100.   createConstraint: function(aObject) {
  101.     var builder = Cc["@songbirdnest.com/Songbird/Library/ConstraintBuilder;1"]
  102.                     .createInstance(Ci.sbILibraryConstraintBuilder);
  103.     aObject.forEach(function(aGroup, aIndex) {
  104.       aGroup.forEach(function(aPair) {
  105.         var property = aPair[0];
  106.         var values = aPair[1];
  107.         var enumerator = {
  108.           a: values,
  109.           i: 0,
  110.           hasMore: function() {
  111.             return this.i < this.a.length;
  112.           },
  113.           getNext: function() {
  114.             return this.a[this.i++];
  115.           }
  116.         };
  117.         builder.includeList(property, enumerator);
  118.       });
  119.       if (aIndex < aObject.length - 1) {
  120.         builder.intersect();
  121.       }
  122.     });
  123.     return builder.get();
  124.   },
  125.  
  126.   createStandardSearchConstraint: function(aSearchString) {
  127.     if (aSearchString == "" || !aSearchString) 
  128.       return null;
  129.     var builder = Cc["@songbirdnest.com/Songbird/Library/ConstraintBuilder;1"]
  130.                     .createInstance(Ci.sbILibraryConstraintBuilder);
  131.     var a = aSearchString.split(" ");
  132.     var first = true;
  133.     for (var i = 0; i < a.length; i++) {
  134.       if (a[i] && a[i] != "") {
  135.         if (!first) {
  136.           builder.intersect();
  137.         }
  138.         builder.include(SBProperties.artistName, a[i]);
  139.         builder.include(SBProperties.albumName, a[i]);
  140.         builder.include(SBProperties.trackName, a[i]);
  141.         first = false;
  142.       }
  143.     }
  144.     return builder.get();
  145.   },
  146.  
  147.   createStandardMediaListView: function(aMediaList, aSearchString) {
  148.     var mediaListView = aMediaList.createView();
  149.  
  150.     // Get the sort for this list by parsing the list's column spec.  Then hit
  151.     // the property manager to see if there is a special sort profile for this
  152.     // ID
  153.     var parser = new ColumnSpecParser(LibraryUtils.webLibrary, null);
  154.     if (parser.sortID) {
  155.       var pm =
  156.         Components.classes["@songbirdnest.com/Songbird/Properties/PropertyManager;1"]
  157.                   .getService(Components.interfaces.sbIPropertyManager);
  158.       var sort = pm.getPropertySort(parser.sortID, parser.sortIsAscending);
  159.       mediaListView.setSort(sort);
  160.     }
  161.     
  162.     // By default, we never want to show lists and hidden 
  163.     // things in the playlist
  164.     mediaListView.filterConstraint = LibraryUtils.standardFilterConstraint;
  165.     
  166.     // Set up a standard search filter.  
  167.     // It can always be replaced later.
  168.     var filter = mediaListView.cascadeFilterSet;
  169.     filter.appendSearch(["*"], 1);
  170.    
  171.     if (aSearchString) {
  172.       // Set the search 
  173.       var searchArray = aSearchString.split(" ");
  174.       filter.set(0, searchArray, searchArray.length);
  175.     } else { 
  176.       // Or not.
  177.       filter.set(0, [], 0);
  178.     }
  179.  
  180.     return mediaListView;
  181.   }
  182. }
  183.  
  184.  
  185.  
  186. /**
  187.  * \class LibraryUtils.BatchHelper
  188.  * \brief Helper object for monitoring the state of 
  189.  *        batch library operations.
  190.  */
  191. LibraryUtils.BatchHelper = function() {
  192.   this._depth = 0;
  193. }
  194.  
  195. LibraryUtils.BatchHelper.prototype.begin =
  196. function BatchHelper_begin()
  197. {
  198.   this._depth++;
  199. }
  200.  
  201. LibraryUtils.BatchHelper.prototype.end =
  202. function BatchHelper_end()
  203. {
  204.   this._depth--;
  205.   if (this._depth < 0) {
  206.     throw new Error("Invalid batch depth!");
  207.   }
  208. }
  209.  
  210. LibraryUtils.BatchHelper.prototype.depth =
  211. function BatchHelper_depth()
  212. {
  213.   return this._depth;
  214. }
  215.  
  216. LibraryUtils.BatchHelper.prototype.isActive =
  217. function BatchHelper_isActive()
  218. {
  219.   return this._depth > 0;
  220. }
  221.  
  222.  
  223.  
  224. /**
  225.  * \class LibraryUtils.MultiBatchHelper
  226.  * \brief Helper object for monitoring the state of 
  227.  *        batch operations in multiple libraries
  228.  */
  229. LibraryUtils.MultiBatchHelper = function() {
  230.   this._libraries = {};
  231. }
  232.  
  233. LibraryUtils.MultiBatchHelper.prototype.get =
  234. function MultiBatchHelper_get(aLibrary)
  235. {
  236.   var batch = this._libraries[aLibrary.guid];
  237.   if (!batch) {
  238.     batch = new LibraryUtils.BatchHelper();
  239.     this._libraries[aLibrary.guid] = batch;
  240.   }
  241.   return batch;
  242. }
  243.  
  244. LibraryUtils.MultiBatchHelper.prototype.begin =
  245. function MultiBatchHelper_begin(aLibrary)
  246. {
  247.   var batch = this.get(aLibrary);
  248.   batch.begin();
  249. }
  250.  
  251. LibraryUtils.MultiBatchHelper.prototype.end =
  252. function MultiBatchHelper_end(aLibrary)
  253. {
  254.   var batch = this.get(aLibrary);
  255.   batch.end();
  256. }
  257.  
  258. LibraryUtils.MultiBatchHelper.prototype.depth =
  259. function MultiBatchHelper_depth(aLibrary)
  260. {
  261.   var batch = this.get(aLibrary);
  262.   return batch.depth();
  263. }
  264.  
  265. LibraryUtils.MultiBatchHelper.prototype.isActive =
  266. function MultiBatchHelper_isActive(aLibrary)
  267. {
  268.   var batch = this.get(aLibrary);
  269.   return batch.isActive();
  270. }
  271.  
  272.  
  273.  
  274.  
  275. /**
  276.  * \class LibraryUtils.RemovalMonitor
  277.  * \brief Helps track removal/deletion of medialists.
  278.  * \param aCallback An object with an onMediaListRemoved function.
  279.  */
  280. LibraryUtils.RemovalMonitor = function(aCallback) {
  281.   //dump("RemovalMonitor: RemovalMonitor()\n");
  282.     
  283.   if (!aCallback || !aCallback.onMediaListRemoved) {
  284.     throw new Error("RemovalMonitor() requires a callback object");
  285.   }
  286.  
  287.   this._callback = aCallback;
  288. }
  289.  
  290. LibraryUtils.RemovalMonitor.prototype = {
  291.  
  292.   // An object with an onMediaListRemoved function
  293.   _callback: null,
  294.  
  295.   // MediaList GUID to monitor for removal
  296.   _targetGUID: null,
  297.   
  298.   // Library that owns the target MediaList
  299.   _library: null,
  300.   
  301.   _libraryManager: null,
  302.   _batchHelper: null,
  303.   
  304.   // Flag to indicate that the target item
  305.   // was deleted in a batch operation
  306.   _removedInBatch: false,
  307.  
  308.  
  309.   /**
  310.    * Watch for removal of the given sbIMediaList.
  311.    * Pass null to stop listening.
  312.    */
  313.   setMediaList:  function RemovalMonitor_setMediaList(aMediaList) {
  314.     //dump("RemovalMonitor: RemovalMonitor.setMediaList()\n");
  315.     this._removedInBatch = false;
  316.   
  317.     // If passed a medialist, hook up listeners
  318.     if (aMediaList instanceof Ci.sbIMediaList) {
  319.       
  320.       // Listen to the library if we aren't doing so already
  321.       if (aMediaList.library != this._library) {
  322.         if (this._library && this._library.guid != this._targetGUID) {
  323.           this._library.removeListener(this);
  324.         }
  325.         
  326.         this._library = aMediaList.library
  327.       
  328.         // If this is a list within a library, then
  329.         // we need to listen for clear/remove in the
  330.         // library
  331.         if (!(aMediaList instanceof Ci.sbILibrary)) {
  332.           this._batchHelper = new LibraryUtils.BatchHelper();
  333.  
  334.           var flags = Ci.sbIMediaList.LISTENER_FLAGS_BATCHBEGIN |
  335.                       Ci.sbIMediaList.LISTENER_FLAGS_BATCHEND |
  336.                       Ci.sbIMediaList.LISTENER_FLAGS_AFTERITEMREMOVED |
  337.                       Ci.sbIMediaList.LISTENER_FLAGS_LISTCLEARED;
  338.                       
  339.           this._library.addListener(this, false, flags, null);
  340.         }
  341.       }
  342.       
  343.       if (!this._libraryManager) {
  344.         this._libraryManager = Cc["@songbirdnest.com/Songbird/library/Manager;1"]
  345.                                 .getService(Ci.sbILibraryManager);
  346.         this._libraryManager.addListener(this);            
  347.       }
  348.         
  349.       // Remember which medialist we are supposed to watch
  350.       this._targetGUID = aMediaList.guid;
  351.     
  352.     
  353.     // If set to null, shut down any listeners
  354.     } else {
  355.       if (this._libraryManager) {
  356.         this._libraryManager.removeListener(this);
  357.         this._libraryManager = null;
  358.       }
  359.       
  360.       if (this._library) {
  361.         this._library.removeListener(this);
  362.         this._library = null;
  363.       }
  364.       this._batchHelper = null;
  365.       this._targetGUID = null;
  366.     } 
  367.   },
  368.  
  369.  
  370.   /**
  371.    * Notifies the listener that the list has been removed, 
  372.    * and then stops monitoring
  373.    */
  374.   _onMediaListRemoved: function RemovalMonitor_onMediaListRemoved() {
  375.     //dump("RemovalMonitor: RemovalMonitor.onMediaListRemoved()\n");
  376.     
  377.     // Our list has been removed. Stop tracking.
  378.     this.setMediaList(null);
  379.     
  380.     // Notify
  381.     this._callback.onMediaListRemoved();
  382.   },
  383.  
  384.  
  385.   /**
  386.    * \sa sbIMediaListListener
  387.    */
  388.   onItemAdded: function(aMediaList, aMediaItem, aIndex) { return true; },
  389.   onItemUpdated: function(aMediaList, aMediaItem, aProperties) { return true },
  390.   onItemMoved: function(aMediaList, aFromIndex, aToIndex) { return true },
  391.   onBeforeItemRemoved: function(aMediaList, aMediaItem, aIndex) { return true; },
  392.   onAfterItemRemoved: function RemovalMonitor_onAfterItemRemoved(aMediaList,
  393.                                                                  aMediaItem,
  394.                                                                  aIndex)
  395.   {
  396.     //dump("RemovalMonitor: RemovalMonitor.onAfterItemRemoved()\n");
  397.     
  398.     // Do no more if in a batch
  399.     if (this._batchHelper.isActive()) {
  400.       if (aMediaItem.guid == this._targetGUID) {
  401.         this._removedInBatch = true;
  402.       }
  403.       return true;
  404.     }
  405.  
  406.     // If our list was removed, notify
  407.     if (aMediaItem.guid == this._targetGUID) {
  408.       this._onMediaListRemoved();
  409.     }
  410.  
  411.     return false;
  412.   },
  413.   onListCleared: function RemovalMonitor_onListCleared(aMediaList)
  414.   {
  415.     //dump("RemovalMonitor: RemovalMonitor.onListCleared()\n");
  416.  
  417.     // Do no more if in a batch
  418.     if (this._batchHelper.isActive()) {
  419.       this._removedInBatch = true;
  420.       return true;
  421.     }
  422.  
  423.     // The current media list must have been removed, so notify
  424.     this._onMediaListRemoved();
  425.  
  426.     return false;
  427.   },
  428.   
  429.   onBatchBegin: function RemovalMonitor_onBatchBegin(aMediaList)
  430.   {
  431.     this._batchHelper.begin();
  432.   },
  433.   onBatchEnd: function RemovalMonitor_onBatchEnd(aMediaList)
  434.   {
  435.     //dump("RemovalMonitor: RemovalMonitor.onBatchEnd()\n");
  436.     
  437.     this._batchHelper.end();
  438.     // If the batch is still in progress do nothing
  439.     if (this._batchHelper.isActive()) {
  440.       return;
  441.     }
  442.  
  443.     var removed = false;
  444.     
  445.     // If we know our target was removed during the batch, notify
  446.     if (this._removedInBatch) {
  447.       removed = true;
  448.       
  449.     // If we don't know for sure, we need to check
  450.     } else if (this._targetGUID != this._library.guid) {
  451.  
  452.       // Check if our media list was removed
  453.       try {
  454.         this._library.getMediaItem(this._targetGUID);
  455.       } catch (e) {
  456.         removed = true;
  457.       }
  458.     }
  459.  
  460.     this._removedInBatch = false;
  461.  
  462.     if (removed) { 
  463.       this._onMediaListRemoved();    
  464.     }
  465.   },
  466.  
  467.   /**
  468.    * \sa sbILibraryManagerListener
  469.    */
  470.   onLibraryRegistered: function(aLibrary) {},
  471.   onLibraryUnregistered: function RemovalMonitor_onLibraryUnregistered(aLibrary)
  472.   {
  473.     //dump("RemovalMonitor: RemovalMonitor.onLibraryUnregistered()\n");
  474.     // If the current library was unregistered, notify
  475.     if (this._library && this._library.equals(aLibrary)) {
  476.       this._onMediaListRemoved(); 
  477.     }
  478.   },
  479.  
  480.  
  481.   /**
  482.    * \sa nsISupports
  483.    */
  484.   QueryInterface: function RemovalMonitor_QueryInterface(aIID) {
  485.     if (!aIID.equals(Components.interfaces.nsISupports) &&
  486.         !aIID.equals(Components.interfaces.sbILibraryManagerListener) &&
  487.         !aIID.equals(Components.interfaces.sbIMediaListListener)) {
  488.       throw Components.results.NS_ERROR_NO_INTERFACE;
  489.     }
  490.     return this;
  491.   }
  492. }
  493.  
  494. /**
  495.  * \class LibraryUtils.MediaListEnumeratorToArray
  496.  * \brief Enumerates items in a media list and stores them in an array.
  497.  */
  498. LibraryUtils.MediaListEnumeratorToArray = function() {
  499. }
  500.  
  501. LibraryUtils.MediaListEnumeratorToArray.prototype = {
  502.   //
  503.   // Media list enumeration array listener fields.
  504.   //
  505.   //   array                    Enumeration array.
  506.   //
  507.  
  508.   array: null,
  509.  
  510.  
  511.   /**
  512.    * \brief Called when enumeration is about to begin.
  513.    *
  514.    * \param aMediaList - The media list that is being enumerated.
  515.    *
  516.    * \return true to begin enumeration, false to cancel.
  517.    */
  518.  
  519.   onEnumerationBegin: function DSW_MLEAL_onEnumerationBegin(aMediaList) {
  520.     // Initialize the enumeration array.
  521.     this.array = [];
  522.     return Ci.sbIMediaListEnumerationListener.CONTINUE;
  523.   },
  524.  
  525.  
  526.   /**
  527.    * \brief Called once for each item in the enumeration.
  528.    *
  529.    * \param aMediaList - The media list that is being enumerated.
  530.    * \param aMediaItem - The media item.
  531.    *
  532.    * \return true to continue enumeration, false to cancel.
  533.    */
  534.  
  535.   onEnumeratedItem: function DSW_MLEAL_onEnumeratedItem(aMediaList,
  536.                                                         aMediaItem) {
  537.     // Add the item to the enumeration array.
  538.     this.array.push(aMediaItem);
  539.     return Ci.sbIMediaListEnumerationListener.CONTINUE;
  540.   },
  541.  
  542.  
  543.   /**
  544.    * \brief Called when enumeration has completed.
  545.    *
  546.    * \param aMediaList - The media list that is being enumerated.
  547.    * \param aStatusCode - A code to determine if the enumeration was successful.
  548.    */
  549.  
  550.   onEnumerationEnd: function DSW_MLEAL_onEnumerationEnd(aMediaList,
  551.                                                         aStatusCode) {
  552.   }
  553. }
  554.  
  555. /**
  556.  * This function is a big ugly hack until we get x-mtp channel working
  557.  * We're punting for now and making mtp item editable, but they aren't
  558.  * as far as the track editor is concerned.
  559.  * presence of the device ID property means we're dealing with an MTP
  560.  * device
  561.  * TODO: XXX Remove this function when x-mtp channel lands and replace
  562.  * it's calls with item.userEditable
  563.  */
  564. LibraryUtils.canEditMetadata = function (aItem) {
  565.   var editable = aItem.userEditable;
  566.   if (editable) { 
  567.     try {
  568.       var contentSrc = aItem.contentSrc;
  569.       if (contentSrc.scheme == "x-mtp")
  570.         editable = false;
  571.     } catch (e) {
  572.       // Errors means it's not x-mtp and edtible
  573.     }
  574.   }
  575.   return editable;
  576. }
  577.  
  578.